package My

import (
	"fmt"
	"db2s/global"
	"reflect"
	"strconv"
	"strings"
)

func getMiddleBracket(s string) (p string) {
	switch {
	case strings.HasPrefix(s, "(") && strings.HasSuffix(s, ")"):
		p = s[1 : len(s)-1]
	case !strings.HasPrefix(s, "(") && strings.Contains(s, "(") && strings.HasSuffix(s, ")"):
		bef := strings.Index(s, "(")
		aft := strings.Index(s, ")")
		if bef != -1 && aft != -1 {
			p = s[bef+1 : aft]
		} else {
			p = s
		}
	default:
		p = s
	}
	return
}
func funcParameterSplit(s string) (r []string) {
	var splitSymbol = ","
	lastCommaIndex := strings.LastIndex(s, splitSymbol)
	// 如果找到逗号，则进行切割
	if lastCommaIndex != -1 {
		r = append(r, strings.Split(s[:lastCommaIndex], splitSymbol)...) // 前部分
		r = append(r, s[lastCommaIndex+1:])                              // 后部分
	} else {
		r = append(r, s) // 如果没有逗号，则只添加原字符串
	}
	return
}

func timeTypeDefaultCurrentTimeStampValueConvert(s string) (r string) {
	switch s {
	case "datetime(0)":
		return "(CONCAT(CURRENT_TIMESTAMP(0)))"
	case "datetime(1)":
		return "(CONCAT(CURRENT_TIMESTAMP(1)))"
	case "datetime(2)":
		return "(CONCAT(CURRENT_TIMESTAMP(2)))"
	case "datetime(3)":
		return "(CONCAT(CURRENT_TIMESTAMP(3)))"
	case "datetime(4)":
		return "(CONCAT(CURRENT_TIMESTAMP(4)))"
	case "datetime(5)":
		return "(CONCAT(CURRENT_TIMESTAMP(5)))"
	case "datetime(6)":
		return "(CONCAT(CURRENT_TIMESTAMP(6)))"
	}
	return
}
func bitTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	if !strings.EqualFold(ct.Format, "b01") {
		return
	}
	cType = fmt.Sprintf("bit(%v)", ct.ShowSize)
	if strings.EqualFold(fmt.Sprintf("%v", ct.ShowSize), "0") {
		cType = fmt.Sprintf("bit")
	}
	return
}
func intTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch ct.DataLength {
	case 1:
		cType = fmt.Sprintf("TINYINT")
		if ct.Symbol {
			cType = fmt.Sprintf("TINYINT unsigned ")
		}
	case 2:
		cType = fmt.Sprintf("SMALLINT")
		if ct.Symbol {
			cType = fmt.Sprintf("SMALLINT unsigned ")
		}
	case 3:
		cType = fmt.Sprintf("MEDIUMINT")
		if ct.Symbol {
			cType = fmt.Sprintf("MEDIUMINT unsigned ")
		}
	case 4:
		cType = fmt.Sprintf("INT")
		if ct.Symbol {
			cType = fmt.Sprintf("INT unsigned ")
		}
	case 8:
		cType = fmt.Sprintf("BIGINT")
		if ct.Symbol {
			cType = fmt.Sprintf("BIGINT unsigned ")
		}
	}
	cByte = ct.DataLength
	return
}
func floatTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	var typeName string
	switch fmt.Sprintf("%v", reflect.TypeOf(ct.MaxSize)) {
	case "int":
		typeName = "float"
		cType = fmt.Sprintf("%v(%v)", typeName, ct.ShowSize)
		if ct.MaxSize.(int) > 23 && ct.MaxSize.(int) <= 54 {
			typeName = "double"
			cType = fmt.Sprintf("%v", typeName)
		}
	}
	cByte = 4
	return
}
func doubleTypeConvert(_ global.TableMeta) (cType string, cByte int) {
	//double不需要指定长度
	cType = fmt.Sprintf("double")
	cByte = 8
	return
}

func decimalTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	//fmt.Println("--------mysql:", Ss.ColumnName, global.AnyToJsonString(Ss.ConvertColumnType))
	if s.AutoIncrement {
		cType = fmt.Sprintf("bigint")
		cByte = 8
		return
	}
	switch ct.Format {
	case "(max,max)":
		cType = fmt.Sprintf("decimal(65,30)")
		cByte = CalculateDecimalSize(65)
		return
	case "Precision", "Precision,(-Scale)":
		if strings.EqualFold(fmt.Sprintf("%v", reflect.TypeOf(ct.ShowSize)), "int") {
			var showSize int
			if ct.ShowSize.(int) > 65 {
				showSize = 65
			} else {
				showSize = ct.ShowSize.(int)
			}
			cType = fmt.Sprintf("decimal(%v,0)", showSize)
			cByte = CalculateDecimalSize(int64(showSize))
		}
		return
	case "Precision,Scale", "Scale,Precision":
		cType = fmt.Sprintf("decimal(%v)", ct.ShowSize)
		if strings.Contains(ct.ShowSize.(string), ",") {
			h := strings.Split(ct.ShowSize.(string), ",")[0]
			if total, err := strconv.Atoi(h); err == nil {
				cByte = CalculateDecimalSize(int64(total))
			}
		}
	default:

	}
	return
}
func charTypeConvert(s global.TableMeta, factor int) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch {
	case ct.ShowSize.(int) <= 255:
		cType = fmt.Sprintf("char(%v)", ct.ShowSize)
		cByte = ct.ShowSize.(int) * factor
	default:
		cType = fmt.Sprintf("varchar(%v)", ct.ShowSize)
		cByte = ct.ShowSize.(int)*factor + 2
	}
	return
}
func varcharTypeConvert(s global.TableMeta, factor int) (cType string, cByte int) {
	ct := s.ConvertColumnType
	cType = fmt.Sprintf("varchar(%v)", ct.ShowSize)
	cByte = ct.ShowSize.(int) * factor
	if ct.ShowSize.(int) > 255 {
		cByte = cByte + 2
	} else {
		cByte = cByte + 1
	}
	return
}
func timeTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch fmt.Sprintf("%v", reflect.TypeOf(ct.ShowSize)) {
	case "int":
		switch ct.Format {
		case "year-month-day":
			cType = fmt.Sprintf("date")
			cByte = 3
		case "hour:minute:second":
			cType = fmt.Sprintf("time(0)")
			cByte = 3
		case "hour:minute:second.milli(1)", "hour:minute:second.milli(2)", "hour:minute:second.milli(3)":
			cType = fmt.Sprintf("time(%v)", ct.ShowSize)
			cByte = 3
		case "hour:minute:second.micro(1)", "hour:minute:second.micro(2)", "hour:minute:second.micro(3)":
			cType = fmt.Sprintf("time(%v)", ct.ShowSize)
			cByte = 3
		case "hour:minute:second.nano(1)", "hour:minute:second.nano(2)", "hour:minute:second.nano(3)":
			cType = fmt.Sprintf("time(6)")
			cByte = 3
		case "year":
			cType = fmt.Sprintf("year")
			cByte = 1
		case "year-month-day hour:minute:second":
			cType = fmt.Sprintf("datetime(%v)", ct.ShowSize)
			cByte = 8
		case "year-month-day hour:minute:second.milli(1)", "year-month-day hour:minute:second.milli(2)", "year-month-day hour:minute:second.milli(3)":
			cType = fmt.Sprintf("datetime(%v)", ct.ShowSize)
			cByte = 8
		case "year-month-day hour:minute:second.micro(1)", "year-month-day hour:minute:second.micro(2)", "year-month-day hour:minute:second.micro(3)":
			cType = fmt.Sprintf("datetime(%v)", ct.ShowSize)
			cByte = 8
		case "year-month-day hour:minute:second.nano(1)", "year-month-day hour:minute:second.nano(2)", "year-month-day hour:minute:second.nano(3)":
			cType = fmt.Sprintf("datetime(6)")
			cByte = 8
		}
	}
	return
}
func timestampTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch fmt.Sprintf("%v", reflect.TypeOf(ct.ShowSize)) {
	case "int":
		switch ct.Format {
		case "year-month-day hour:minute:second.utc":
			cType = fmt.Sprintf("timestamp(%v)", ct.ShowSize)
		case "year-month-day hour:minute:second.milli(1)", "year-month-day hour:minute:second.milli(2)", "year-month-day hour:minute:second.milli(3)":
			cType = fmt.Sprintf("datetime(%v)", ct.ShowSize)
		case "year-month-day hour:minute:second.milli(1).utc", "year-month-day hour:minute:second.milli(2).utc", "year-month-day hour:minute:second.milli(3).utc":
			cType = fmt.Sprintf("timestamp(%v)", ct.ShowSize)
		case "year-month-day hour:minute:second.micro(1).utc", "year-month-day hour:minute:second.micro(2).utc", "year-month-day hour:minute:second.micro(3).utc":
			cType = fmt.Sprintf("timestamp(%v)", ct.ShowSize)
		case "year-month-day hour:minute:second.micro(1)", "year-month-day hour:minute:second.micro(2)", "year-month-day hour:minute:second.micro(3)":
			cType = fmt.Sprintf("datetime(%v)", ct.ShowSize)
		case "year-month-day hour:minute:second.nano(1)", "year-month-day hour:minute:second.nano(2)", "year-month-day hour:minute:second.nano(3)":
			cType = fmt.Sprintf("datetime(6)")
		case "year-month-day hour:minute:second.nano(1).utc", "year-month-day hour:minute:second.nano(2).utc", "year-month-day hour:minute:second.nano(3).utc":
			cType = fmt.Sprintf("timestamp(6)")
		}
	}
	cByte = 4
	return
}
func textTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch fmt.Sprintf("%v", reflect.TypeOf(ct.ShowSize)) {
	case "int":
		switch {
		case ct.DataLength <= 255:
			cType = fmt.Sprintf("TINYTEXT")
		case ct.DataLength <= 65535:
			cType = fmt.Sprintf("TEXT")
		case ct.DataLength <= 16777215:
			cType = fmt.Sprintf("MEDIUMTEXT")
		case ct.DataLength <= 4294967295:
			cType = fmt.Sprintf("LONGTEXT")
		default:
			cType = fmt.Sprintf("LONGTEXT")
		}
	}
	return
}
func jsonTypeConvert(s global.TableMeta) (cType string, cByte int) {
	switch fmt.Sprintf("%v", s.ConvertColumnType.Type) {
	case "json":
		switch {
		default:
			cType = fmt.Sprintf("json")
		}

	}
	return
}
func enumTypeConvert(s global.TableMeta) (cType string, cByte int) {
	switch fmt.Sprintf("%v", s.ConvertColumnType.Type) {
	case "enum":
		switch {
		default:
			cType = fmt.Sprintf("%v", s.DataType)
		}
	}
	return
}
func lobTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch fmt.Sprintf("%v", reflect.TypeOf(ct.ShowSize)) {
	case "int":
		if len(ct.Format) > 0 {
			cType = ct.Format
		} else {
			switch {
			case ct.ShowSize.(int) <= 255:
				cType = fmt.Sprintf("BINARY(%v)", ct.ShowSize)
			case ct.ShowSize.(int) > 255 && ct.ShowSize.(int) <= 65535:
				cType = fmt.Sprintf("VARBINARY(%v)", ct.ShowSize)
			case ct.ShowSize.(int) > 65535 && ct.ShowSize.(int) <= 16777215:
				cType = fmt.Sprintf("MEDIUMBLOB")
			case ct.ShowSize.(int) > 16777215 && ct.ShowSize.(int) <= 4294967295:
				cType = fmt.Sprintf("LONGBLOB")
			}
		}
	}
	return
}
func gisTypeConvert(s global.TableMeta) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch ct.Format {
	case "point": //点
		cType = fmt.Sprintf("POINT")
	case "multiPoint": //点集合
		cType = fmt.Sprintf("MULTIPOINT")
	case "line": //线
		cType = fmt.Sprintf("LINESTRING")
	case "multiLine": //线集合
		cType = fmt.Sprintf("MULTILINESTRING")
	case "polygon": //多边形
		cType = fmt.Sprintf("POLYGON")
	case "multiPolygon": //多边形集合
		cType = fmt.Sprintf("MULTIPOLYGON")
	case "geometry": //三维空间   主要单位是度，用于立体
		cType = fmt.Sprintf("GEOMETRY")
	case "multiGeometry":
		cType = fmt.Sprintf("GEOMETRYCOLLECTION")
	}
	return
}

/*
TypeConvert 目标端根据通用协议进行数据类型转换
*/
func TypeConvert(s global.TableMeta, factor int) (cType string, cByte int) {
	ct := s.ConvertColumnType
	switch ct.Type {
	case "bit":
		return bitTypeConvert(s)
	case "int":
		return intTypeConvert(s)
	case "decimal":
		return decimalTypeConvert(s)
	case "float":
		return floatTypeConvert(s)
	case "double":
		return doubleTypeConvert(s)
	case "char":
		return charTypeConvert(s, factor)
	case "varchar":
		return varcharTypeConvert(s, factor)
	case "time":
		return timeTypeConvert(s)
	case "timestamp":
		return timestampTypeConvert(s)
	case "text":
		return textTypeConvert(s)
	case "json":
		return jsonTypeConvert(s)
	case "enum":
		return enumTypeConvert(s)
	case "lob":
		return lobTypeConvert(s)
	case "gis":
		return gisTypeConvert(s)
	}
	return
}
func DefaultValueConvert(convertDataType, dataType string, l global.ConvertDefaultValue) string {
	switch {
	case len(l.Format) == 0:
		switch l.NewValue {
		case "<entry>", "''":
			return "''"
		case "<null>", "NULL":
			return "NULL"
		default:
			if !DefaultValueQuotation(dataType) {
				return fmt.Sprintf("'%v'", l.NewValue)
			}
			return l.NewValue
		}
	case len(l.Format) > 0:
		switch l.Format {
		case "year-month-day hour:minute:second.micro(3).utc", "year-month-day hour:minute:second.micro(3)", "year-month-day hour:minute:second.nano(1)", "year-month-day hour:minute:second.nano(1).utc", "year-month-day hour:minute:second.nano(2)", "year-month-day hour:minute:second.nano(2).utc", "year-month-day hour:minute:second.nano(3)", "year-month-day hour:minute:second.nano(3).utc":
			switch dataType {
			case "varchar":
				return "(CONCAT(CURRENT_TIMESTAMP(6)))"
			case "time":
				return timeTypeDefaultCurrentTimeStampValueConvert(convertDataType)
			default:
				return "CURRENT_TIMESTAMP(6)"
			}
		case "year-month-day hour:minute:second.micro(2).utc", "year-month-day hour:minute:second.micro(2)":
			switch dataType {
			case "varchar":
				return "(CONCAT(CURRENT_TIMESTAMP(5)))"
			case "time":
				return timeTypeDefaultCurrentTimeStampValueConvert(convertDataType)
			default:
				return "CURRENT_TIMESTAMP(5)"
			}
		case "year-month-day hour:minute:second.micro(1).utc", "year-month-day hour:minute:second.micro(1)":
			switch dataType {
			case "varchar":
				return "(CONCAT(CURRENT_TIMESTAMP(4)))"
			case "time":
				return timeTypeDefaultCurrentTimeStampValueConvert(convertDataType)
			default:
				return "CURRENT_TIMESTAMP(4)"
			}
		case "year-month-day hour:minute:second.milli(3).utc", "year-month-day hour:minute:second.milli(3)":
			switch dataType {
			case "varchar":
				return "(CONCAT(CURRENT_TIMESTAMP(3)))"
			case "time":
				return timeTypeDefaultCurrentTimeStampValueConvert(convertDataType)
			default:
				return "CURRENT_TIMESTAMP(3)"
			}
		case "year-month-day hour:minute:second.milli(2).utc", "year-month-day hour:minute:second.milli(2)":
			switch dataType {
			case "varchar":
				return "(CONCAT(CURRENT_TIMESTAMP(2)))"
			case "time":
				return timeTypeDefaultCurrentTimeStampValueConvert(convertDataType)
			default:
				return "CURRENT_TIMESTAMP(2)"
			}
		case "year-month-day hour:minute:second.milli(1).utc", "year-month-day hour:minute:second.milli(1)":
			switch dataType {
			case "varchar":
				return "(CONCAT(CURRENT_TIMESTAMP(1)))"
			case "time":
				return timeTypeDefaultCurrentTimeStampValueConvert(convertDataType)
			default:
				return "CURRENT_TIMESTAMP(1)"
			}
		case "year-month-day hour:minute:second", "year-month-day hour:minute:second.value":
			var format = "'%Y-%m-%d %H:%i:%s'"
			switch {
			case dataType == "varchar":
				return fmt.Sprintf("(DATE_FORMAT(CURRENT_TIMESTAMP(0),%v))", format)
			case dataType == "time" && strings.HasSuffix(l.Format, ".value"):
				return fmt.Sprintf("(STR_TO_DATE(%v, %v))", funcParameterSplit(getMiddleBracket(l.OldValue))[0], format)
			default:
				r := getMiddleBracket(convertDataType)
				if len(r) == 0 {
					return "CURRENT_TIMESTAMP(0)"
				}
				return fmt.Sprintf("CURRENT_TIMESTAMP(%v)", r)
			}
		case "year-month-day 00:00:00":
			var format = "'%Y-%m-%d 00:00:00'"
			r := getMiddleBracket(convertDataType)
			if len(r) == 0 {
				r = "0"
			}
			switch {
			case dataType == "varchar":
				return fmt.Sprintf("(DATE_FORMAT(CURRENT_TIMESTAMP(%v),%v))", r, format)
			case dataType == "time" && strings.HasSuffix(l.Format, ".value"):
				return fmt.Sprintf("(STR_TO_DATE(%v, %v))", funcParameterSplit(getMiddleBracket(l.OldValue))[0], format)
			default:
				return fmt.Sprintf("(DATE_FORMAT(CURRENT_TIMESTAMP(%v),%v))", r, format)
			}

		case "hour:minute:second":
			switch dataType {
			case "varchar":
				return "(DATE_FORMAT(CURRENT_TIME,'%H:%i:%s'))"
			default:
				return "CURRENT_TIME"
			}
		case "current_user@host":
			return "USER()"
		case "current_sys_user":
			return "SYSTEM_USER()"
		case "randValue":
			return "RAND()"
		case "uuid":
			return "UUID()"
		case "sys_guid":
			return "(sys_guid())"
		case "current_user":
			return "CURRENT_USER()"
		case "current_id":
			return "CONNECTION_ID()"
		default:
			return l.Format
		}
	}
	return ""
}
